package com.chwl.framework.springboot.grpc.config;

import com.chwl.framework.grpc.annotation.GrpcService;
import com.chwl.framework.springboot.grpc.GrpcServerRunner;
import com.chwl.framework.springboot.grpc.service.CommonService;
import io.grpc.ServerBuilder;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.*;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.CollectionUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

@Configuration
@EnableConfigurationProperties(GrpcProperties.class)
public class GrpcAutoConfiguration {

    private static final Logger log = Logger.getLogger(GrpcAutoConfiguration.class.getName());

    private final AbstractApplicationContext applicationContext;

    private final GrpcProperties grpcProperties;

    public GrpcAutoConfiguration(AbstractApplicationContext applicationContext, GrpcProperties grpcProperties) {
        this.applicationContext = applicationContext;
        this.grpcProperties = grpcProperties;
    }

    @Bean
    public CommonService commonService() {
        return new CommonService(applicationContext);
    }

    @Bean
    @ConditionalOnProperty(value = "spring.grpc.enable", havingValue = "true")
    public GrpcServerRunner grpcServerRunner() {
        return new GrpcServerRunner(ServerBuilder.forPort(grpcProperties.getPort()), commonService(), grpcProperties);
    }

    @Configuration
    @Import({GrpcAutoConfiguration.AutoConfiguredGrpcServiceScannerRegistrar.class})
    public class AutoConfiguredGrpcServiceScanner {

    }

    /**
     * 自动扫描 @GrpcService 注解的接口，生成动态代理类，注入的 Spring 容器
     */
    public static class AutoConfiguredGrpcServiceScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {

        private BeanFactory beanFactory;

        private ResourceLoader resourceLoader;

        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory = beanFactory;
        }

        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }

        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            ClassPathBeanDefinitionScanner scanner = new ClassPathGrpcServiceScanner(registry);
            scanner.setResourceLoader(this.resourceLoader);
            scanner.addIncludeFilter(new AnnotationTypeFilter(GrpcService.class));

            Set<BeanDefinition> beanDefinitions = scanPackages(scanner);

            ProxyUtil.registBeans(beanFactory, beanDefinitions);
        }

        /**
         * 包扫描
         */
        private Set<BeanDefinition> scanPackages(ClassPathBeanDefinitionScanner scanner) {
            List<String> packages = AutoConfigurationPackages.get(beanFactory);
            Set<BeanDefinition> beanDefinitions = new HashSet<>();
            if (CollectionUtils.isEmpty(packages)) {
                return beanDefinitions;
            }
            packages.forEach(pack -> beanDefinitions.addAll(scanner.findCandidateComponents(pack)));
            return beanDefinitions;
        }

    }
}